package com.bckj.fastboot.core.util;

import com.bckj.fastboot.core.lang.func.VoidFunc2;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;

@SuppressWarnings("unchecked")
public class DataAssembler<T> {

    private final List<T> source;
    private final List<JoinCarrier<?, ?>> joinCarriers = new ArrayList<>();

    private Consumer<T> consumer = t -> { };

    public DataAssembler(List<T> source) {
        this.source = source;
    }

    public static <T> DataAssembler<T> of(List<T> source) {
        return new DataAssembler<>(source);
    }

    public <K, O> DataAssembler<T> join(Map<K, O> target, Function<T, K> sourceKeyFunc, Function<O, K> targetKeyFunc,
                                        VoidFunc2<T, O> consumer) {
        Assert.notEmpty(target, "target can not be empty");
        Assert.notNull(sourceKeyFunc, "sourceKeyFunc can not be empty");
        Assert.notNull(targetKeyFunc, "targetKeyFunc can not be empty");
        Assert.notNull(consumer, "consumer can not be empty");
        JoinCarrier<K, O> carrier = new JoinCarrier<>();
        carrier.target = target;
        carrier.sourceKeyFunc = sourceKeyFunc;
        carrier.targetKeyFunc = targetKeyFunc;
        carrier.consumer = consumer;
        joinCarriers.add(carrier);
        return this;
    }

    public DataAssembler<T> consume(Consumer<T> consumer) {
        this.consumer = consumer;
        return this;
    }

    public void assemble() {
        for (T t : source) {
            for (JoinCarrier<?, ?> carrier : joinCarriers) {
                Map<?, ?> target = carrier.target;
                Object key = carrier.sourceKeyFunc.apply(t);
                Object value = target.get(key);
                VoidFunc2<T, Object> consumer = (VoidFunc2<T, Object>) carrier.consumer;
                consumer.call(t, value);
            }
            consumer.accept(t);
        }
    }

    class JoinCarrier<K, O> {
        Map<K, O> target;
        Function<T, K> sourceKeyFunc;
        Function<O, K> targetKeyFunc;
        VoidFunc2<T, O> consumer;
    }
}
